#pragma once

#include<iostream>
#include<cstring>
#include<functional>
#include<unistd.h>
#include<sys/types.h>
#include<netine/in.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<pthread.h>
#include<memory>

#include"Log.hpp"
#include"InetAddr.hpp"
#include"Comm.hpp"

namespace socket_ns
{
    using namespace log_ns;
    class Socket;
    using SockSptr=std::shared_ptr<Socket>;
    const static int gblcklog=8;

    //模板方法
     class Socket
    {
    public:
        virtual void CreateSocketOrDie() = 0;
        virtual void CreateBindOrDie(uint16_t port) = 0;
        virtual void CreateListenOrDie(int backlog = gblcklog) = 0;
        virtual int Accepter(InetAddr *cliaddr, int *code) = 0;
        virtual bool Conntecor(const std::string &peerip, uint16_t peerport) = 0;
        virtual int Sockfd() = 0;
        virtual void Close() = 0;
        virtual ssize_t Recv(std::string *out) = 0;
        virtual ssize_t Send(const std::string &in) = 0;

        virtual void ReUseAddr() = 0;

    public:
        void BuildListtenSocket(uint16_t port)
        {
            CreateSocketOrDie();
            ReUseAddr();
            CreateBindOrDie();
            CreateListenOrDie();
        }

        bool BuildClientSocket(const std::String& peerip,uint16_t peerport)
        {
            CreateSocketOrDie();
            return Conntecor(peerip,peerport);
        }
    };

    class TcpServer:public Socket
    {
    public:
        TcpServer()
        {        }
        TcpServer(int sockfd):_sockfd(sockfd)
        {}
        ~TcpServer()
        {}

        void CreateSocketOrDie() override
        {
            _sockfd=socket(AF_INET,SOCK_STREA,0);
            if(_sockfd<0)
            {
                LOG(FATAL,"socket create error\n");
                exit(SOCKET_ERROR);
            }
            SetNonBlock(_sockfd);
            LOG(INFO,"socket create success, sockfd: %d\n",_sockfd);
        }

        void CreateBindOrDie(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(port);
            local.sin_addr.s_addr=INADDR_ANY;

            if(bind(_sockfd,(struct sockaddr*)& local,sizeof(local))<0)
            {
                LOG(FATAL,"bind error\n");
                exit(BIND_ERROR);
            }
        }

        int Accepter(InetAddr* cliaddr,int* code) override
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);

            int sockfd=accept(_sockfd,(struct sockaddr*)&client,&len);
            if(sockfd<0)
            {
                return -1;
            }
            SerNOnBlock(sockfd);
            *cliaddr=InetAddr(client);
            LOG(INFO,"get a new link,client info : %s,sockfd is: %d\n",cliaddr->AddrStr().c_str(),sockfd);   
        
            return sockfd;
        }

        bool Conntecor(const std::string& peerip,uint16_t peerport) override
        {
            struct sockaddr_in server;
            memeset(&server,0,sizeof(server));
            server.sin_family=AF_INET;
            server.sin_port=htons(peerport);
            inet_pton(peerip.c_str(),&server.sin_addr);
            socklen_t len=sizeof(server);
            int n=connect(_sockfd,(const struct sockaddr*)&server,len);
            if(n<0)
            {
                return false;
            }
            return true;
        }

        int Sockfd()
        {
            return _sockfd;
        }

        void Close()
        {
            if(_sockfd>0)
                close(_sockfd);
        }

        ssize_t Recv(std::string* out) override
        {
            char inbuffer[4096];
            ssize_t n=recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);
            if(n>0)
            {
                inbuffer[n]=0;
                *out=inbuffer;
            }
            return n;
        }

        ssize_t Send(const std::string& in) override
        {
            return send(_sockfd,in.c_str(),in.size(),0);
        }

        void ReUseAddr() override
        {
            int opt=1;
            setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
        }


    private:
        int _sockfd;

    };

}